package wangwenjun.phase2.concurrencypattern.activeobject.dynamicproxy.framework;

import lombok.extern.slf4j.Slf4j;
import wangwenjun.phase2.concurrencypattern.future.Future;
import wangwenjun.phase2.concurrencypattern.future.FutureImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author ChangLiang
 * @date 2020/6/18
 */
@Slf4j
public class ActiveObjectServiceFactory {

    private final static ActiveObjectMessageQueue queue = new ActiveObjectMessageQueue();

    /**
     * 会根据service实例生成一个动态代理实例
     * 其中会用到{@link ActiveObjectInvocationHandler}作为newProxyInstance的InvocationHandler
     * @param service
     * @param <T>
     * @return
     */
    public static <T> T createActiveObject(T service) {
        return (T) Proxy.newProxyInstance(service.getClass().getClassLoader(),
                service.getClass().getInterfaces(),
                new ActiveObjectInvocationHandler<>(service));
    }

    private static class ActiveObjectInvocationHandler<T> implements InvocationHandler{

        private final T service;

        ActiveObjectInvocationHandler(T service) {
            this.service = service;
        }

        /**
         *
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 如果接口方法被@ActiveObjectMethod标记，则会转换为ActiveObjectMessage
            // 注意，这里注解要标注到OrderService interface上 因为其method是OrderService#createOrder
            if (!method.isAnnotationPresent(ActiveObjectMethod.class)) {
                return method.invoke(service, args);
            }
            Object result = null;

            // 检查该方法是否符合规范
            this.checkMethod(method);

            ActiveObjectMessage.Builder builder = new ActiveObjectMessage.Builder()
                    .method(method)
                    .params(args)
                    .service(service);
            if (isReturnFutureType(method)) {
                result = new FutureImpl<>();
                log.info("ActiveObjectServiceFactory#invoke FutureImpl:{}",result.toString());
                builder.future((FutureImpl<Object>) result);
            }
            queue.offer(builder.build());
            return result;
        }

        /**
         * 检查有返回值的方法是否为Future ，
         * @param method
         * @throws IllegalActiveObjectMethod
         */
        private void checkMethod(Method method) throws IllegalActiveObjectMethod {
            if (!isReturnFutureType(method) && !isReturnVoidType(method)) {
                throw new IllegalActiveObjectMethod("the method [" + method.getName() + " return type must be void/Future");
            }
        }

        private boolean isReturnFutureType(Method method) {
            return method.getReturnType().isAssignableFrom(Future.class);
        }

        private boolean isReturnVoidType(Method method) {
            return method.getReturnType().equals(Void.TYPE);
        }
    }
}
